// https://blog.csdn.net/rxbook/article/details/130668154

package main

import "fmt"

// 定义一个非空接口
type UserInterface interface {
	getUserName(uid int) (string, error) //根据uid获取用户名的方法定义,形参uid可省略
	getUserInfo(username string) string  //根据用户名获取用户详细信息的方法定义,形参username可省略
}

// 接口的实现:定义结构体和方法
type users struct {
	age  int
	name string
}

func (u *users) getUserName(uid int) (string, error) {
	return "张三", nil
}
func (u *users) getUserInfo(username string) string {
	return fmt.Sprintf("用户名是:%v", username)
}

// 定义一个空接口, 等价于 interface{}
type EmptyInterface interface {
}

// 接口被定义后,就可以用于声明变量
var ui UserInterface // ui 是一个 UserInterface 接口类型的实例变量,也叫做:接口类型变量,零值是 nil

type myInterface interface {
	method1()
}
type myInt int

func (myInt) method1() {
	println("myInt 的方法 method1")
}

func printNilInterface() {
	// nil接口变量
	// 无论是空接口类型还是非空接口类型变量，一旦变量值为 nil，那么它们内部表示均为(0x0,0x0)，也就是类型信息、数据值信息均为空。因此下面的变量 i 和 err 等值判断为 true
	var i interface{}   // 空接口类型
	var err error       // 非空接口类型
	println(i)          // (0x0,0x0)
	println(err)        // (0x0,0x0)
	println(i == nil)   // true
	println(err == nil) // true
	println(i == err)   // true
}
func printEmptyInterface() {
	var eif1 interface{} // 空接口类型
	var eif2 interface{} // 空接口类型

	//eif1 与 eif2 已经分别被赋值整型值 17 与 18，它们的动态类型信息是相同的（都是 0x1097200），但data指针指向的内存值不同,因此不相等
	eif1 = 17
	eif2 = 18
	println("eif1:", eif1) //eif1: (0x1097200,0x10c7fd0)
	println("eif2:", eif2) //eif2: (0x1097200,0x10c7fd8)
	println(eif1 == eif2)  // false

	//eif2 重新赋值为 17，此时 eif1 和 eif2 不仅存储的动态类型相同（都是 0x1097200）,而且data 指针指向的内存值也相同了,因此相等
	eif2 = 17
	println("eif1:", eif1) //eif1: (0x1097200,0x10c7fd0)
	println("eif2:", eif2) //eif2: (0x1097200,0x10c7fd0)
	println(eif1 == eif2)  // true

	//eif2 重新赋值为 int64 类型的数值17,此时 eif1 和 eif2 存储的动态类型信息不同,就算值相同,依然是不相等的
	eif2 = int64(17)
	println("eif1:", eif1) //eif1: (0x1097200,0x10c7fd0)
	println("eif2:", eif2) //eif2: (0x10972c0,0x10c7fd0)
	println(eif1 == eif2)  // false
}

type T int

func (t T) Error() string {
	return "bad error"
}
func printNonEmptyInterface() {
	var err1 error // 非空接口类型
	var err2 error // 非空接口类型
	err1 = (*T)(nil)
	println("err1:", err1) //err1: (0x10ca8a8,0x0)
	println(err1 == nil)   //false
	err1 = T(5)
	err2 = T(6)
	println("err1:", err1) //err1: (0x10ca888,0x10ca268)
	println("err2:", err2) //err2: (0x10ca888,0x10ca270)
	println(err1 == err2)  //false
	err2 = fmt.Errorf("%d\n", 5)
	println("err1:", err1) //err1: (0x10ca888,0x10ca268)
	println("err2:", err2) //err2: (0x10ca768,0xc00010a230)
	println(err1 == err2)  //false
}

func printEmptyInterfaceAndNonEmptyInterface() {
	var eif interface{} = T(5)
	var err error = T(5)
	println("eif:", eif) //eif: (0x109b360,0x10ca400)
	println("err:", err) //err: (0x10caa18,0x10ca400)
	println(eif == err)  //true
	err = T(6)
	println("eif:", eif) //eif: (0x109b360,0x10ca400)
	println("err:", err) //err: (0x10caa18,0x10ca408)
	println(eif == err)  //false
}

func main() {
	var u = users{}
	fmt.Println(u.getUserName(1))      //张三 <nil>
	fmt.Println(u.getUserInfo("haha")) //用户名是:haha
	println()

	//类型断言（Type Assertion）：通过一个接口类型变量还原它的右值的类型与值信息
	var a int64 = 20
	var i interface{} = a
	v1, ok := i.(int64)
	fmt.Printf("v1:%d, type:%T, ok:%t \n", v1, v1, ok) // v1:20, type:int64, ok:true
	v2 := i.(int64)                                    // 省略ok
	fmt.Printf("v2:%d, type:%T\n", v2, v2)             // v2:20, type:int64

	v3, ok := i.(string)
	fmt.Printf("v3:%s, type:%T, ok:%t \n", v3, v3, ok) // v3:, type:string, ok:false
	// v4 := i.(string)                                // 省略ok,导致 panic: interface conversion: interface {} is int64, not string

	var myi myInt
	var myj interface{} = myi
	v5, ok := myj.(myInterface)
	fmt.Printf("v5: type:%T, ok:%t \n", v5, ok) // v5: type:<nil>, ok:false
	v5.method1()                                // myInt 的方法 method1
	fmt.Printf("v5: type:%T \n", v5)            // v5: type:main.myInt
	println()

	printNilInterface()
	println()
	printEmptyInterface()
	println()
	printNonEmptyInterface()
	println()
	printEmptyInterfaceAndNonEmptyInterface()

}
